Skip to main content

🔧 Step 6: Full CRUD Integration Using Axios

  • Create
  • Read (already done)
  • Update
  • Delete

We'll build a clean and modular API service layer and integrate it step-by-step in the frontend.


🎯 Objective:

Use Axios to connect your frontend with the RESTful API and handle:

  • GET /tasks → fetch all tasks
  • POST /tasks → create task
  • PUT /tasks/:id → update task
  • DELETE /tasks/:id → delete task

📁 A. Update Task Service

Update src/lib/taskService.ts:

import { api } from "./api"

export type Task = {
id: string
title: string
}

// Get all tasks
export async function fetchTasks(): Promise<Task[]> {
const res = await api.get("/tasks")
return res.data
}

// Create a new task
export async function createTask(title: string): Promise<Task> {
const res = await api.post("/tasks", { title })
return res.data
}

// Update a task
export async function updateTask(id: string, title: string): Promise<Task> {
const res = await api.put(`/tasks/${id}`, { title })
return res.data
}

// Delete a task
export async function deleteTask(id: string): Promise<void> {
await api.delete(`/tasks/${id}`)
}

🔁 Adjust the route paths (e.g., /tasks, /api/tasks) depending on your actual backend from GitLab.


✅ B. Update TaskPage.tsx to Use Full CRUD

Update TaskPage.tsx to integrate the service:

import { useEffect, useState } from "react"
import TaskTable from "@/components/task/TaskTable"
import {
fetchTasks,
createTask,
updateTask,
deleteTask,
Task,
} from "@/lib/taskService"

export default function TaskPage() {
const [tasks, setTasks] = useState<Task[]>([])
const [loading, setLoading] = useState(true)
const [editing, setEditing] = useState<{ id: string; title: string } | null>(null)
const [input, setInput] = useState("")

useEffect(() => {
getAllTasks()
}, [])

const getAllTasks = async () => {
try {
const data = await fetchTasks()
setTasks(data)
} catch (err) {
console.error("Fetch error:", err)
} finally {
setLoading(false)
}
}

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()

if (editing) {
const updated = await updateTask(editing.id, input)
setTasks((prev) =>
prev.map((t) => (t.id === editing.id ? updated : t))
)
setEditing(null)
} else {
const newTask = await createTask(input)
setTasks((prev) => [newTask, ...prev])
}

setInput("")
}

const handleEdit = (id: string, title: string) => {
setEditing({ id, title })
setInput(title)
}

const handleDelete = async (id: string) => {
await deleteTask(id)
setTasks((prev) => prev.filter((t) => t.id !== id))
}

return (
<div className="max-w-4xl mx-auto mt-10 space-y-6">
<h1 className="text-3xl font-bold text-center">📋 Task Manager</h1>

{/* FORM */}
<form onSubmit={handleSubmit} className="flex gap-2 mb-4">
<input
className="border px-3 py-2 w-full rounded"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter task title"
/>
<button
className="bg-blue-600 text-white px-4 py-2 rounded"
type="submit"
>
{editing ? "Update" : "Add"}
</button>
</form>

{/* TABLE */}
{loading ? (
<p className="text-center text-gray-500">Loading tasks...</p>
) : (
<TaskTable tasks={tasks} onEdit={handleEdit} onDelete={handleDelete} />
)}
</div>
)
}

🛠️ Update TaskTable.tsx to Accept onEdit and onDelete

type Task = {
id: string
title: string
}

type TaskTableProps = {
tasks: Task[]
onEdit: (id: string, title: string) => void
onDelete: (id: string) => void
}

export default function TaskTable({ tasks, onEdit, onDelete }: TaskTableProps) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">#</TableHead>
<TableHead>Title</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>

<TableBody>
{tasks.length === 0 ? (
<TableRow>
<TableCell colSpan={3} className="text-center py-6 text-gray-500">
No tasks found.
</TableCell>
</TableRow>
) : (
tasks.map((task, index) => (
<TableRow key={task.id}>
<TableCell>{index + 1}</TableCell>
<TableCell>{task.title}</TableCell>
<TableCell className="text-right space-x-2">
<button
className="text-sm text-blue-600 hover:underline"
onClick={() => onEdit(task.id, task.title)}
>
Edit
</button>
<button
className="text-sm text-red-500 hover:underline"
onClick={() => onDelete(task.id)}
>
Delete
</button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
)
}

✅ Summary

You now have full CRUD operations wired up:

  • GET — fetch tasks
  • POST — create new task
  • PUT — update a task
  • DELETE — remove a task

And all of this is done with Axios, ShadCN UI, and a modular service layer.